#include "include/TextureTest.h"
#include "include/App.h"
#include "include/NetworkInterface.h"

#define PROTOCOL_UDP		17
#define WOS_MAX_RX_UDP_PACKET	2048

#ifdef __LINUX__
socklen_t WOS_SIZE_NRA = sizeof(sockaddr_in);
#else
int WOS_SIZE_NRA = sizeof(sockaddr_in);
#endif

using namespace Ei;
using namespace Ogre;

///////////////////////////////////////////////////////////////////////////////
// NetworkBaseMethod base class 
/** Constructor. Initializes WOscReceiverMethod base class. */
NetworkBaseMethod::NetworkBaseMethod(
	WOscContainer* parent,
	NetworkInterface* receiverContext,
	char* methodName,
	char* methodDescription)
:WOscReceiverMethod(
	parent,
	(WOscReceiver*)receiverContext,
	methodName,
	methodDescription)
{}

/** Type-save access to receiver object, which is owning this object.        */
NetworkInterface* NetworkBaseMethod::GetContext()
{
	return (NetworkInterface*)WOscReceiverMethod::GetContext();
}

/** Type-save method interface. All methods inherited from this class get
 * the correct TheNetReturnAddress pointers without caring about the
 * system below.
 */
void NetworkBaseMethod::Method(
	const WOscMessage *message,
	const WOscTimeTag& when,
	const WOscNetReturn* networkReturnAddress)
{
	
	Method(message, when, (TheNetReturnAddress*)networkReturnAddress);
}

///////////////////////////////////////////////////////////////////////////////
// NetworkInterface
//////////////////////////////////////////////////////////////////////////////
/** Constructor. Allocates an UDP receive buffer
 * and initializes the exit flag to "false".
 */
NetworkInterface::NetworkInterface()
{

    // allocate RX buffer
    m_rxbuffer = new char[WOS_MAX_RX_UDP_PACKET];
    m_exit = false;
}

/** Destructor. Removes the address space and deallocates the UDP receive
 * buffer.
 */
NetworkInterface::~NetworkInterface()
{
	// remove address space //
	WOscContainer* as = GetAddressSpace();
	if ( as ) as->RemoveAll();
}

/** Checks the exit flag.
 * \returns
 * True if application/serve exit is requested.
 */
bool NetworkInterface::Exit() const
{
	return m_exit;
}

/** Sets the exit flag, which is used to check for server termination.       */
void NetworkInterface::SetExit()
{
	m_exit = true;
}

/** Sends data over the network.
 * \param data
 * Pointer to buffer containing the data to be sent.
 *
 * \param dataLen
 * Number of bytes in the data buffer.
 *
 * \param networkReturnAddress
 * The destination network address.
 */
void NetworkInterface::NetworkSend(
	const char* data,
	int dataLen,
	const WOscNetReturn* networkReturnAddress)
{
	const TheNetReturnAddress* nra = (const TheNetReturnAddress*)networkReturnAddress;
	int actSend = sendto(
		m_hSocket,
		data,
		dataLen,
		0,
		(struct sockaddr*)&nra->m_addr,
		WOS_SIZE_NRA ) ;
	
	// check if transmission was successful
	if ( dataLen != actSend )
		eilog( "Error sending packet.") ;
}

/** Callback for OSC packets which have caused an exception in the OSC 
 * receiver.
 * \param data
 * Pointer to buffer containing the packet data.
 * \param dataLen
 * Length of the packet data in the buffer.
 * \exception
 * Exception that caused this handler to be called.
 */
void NetworkInterface::HandleOffendingPackets(
	const char* const data,
	int dataLen,
	WOscException* exception)
{
	eilog("HandleOffendingPackets.");
}

/** Callback for messages not matching any address in the local OSC address
 * space.
 * \param msg
 * Message which hasn't been processed.
 * \param networkReturnAddress
 * Network origin of the message.
 */
void NetworkInterface::HandleNonmatchedMessages(
	const WOscMessage* msg,
	const WOscNetReturn* networkReturnAddress)
{
	eilog("Received message with no container defined.");
	int nStr = msg->GetNumStrings();
	int nInt = msg->GetNumInts();
	int nFlt = msg->GetNumFloats();
	
	String s = "";
	if ( !nStr && !nInt && !nFlt )
	{	s =  "OSC message \"" +  
			String((msg->GetOscAddress().GetBuffer()))
			+  "\" received.";
	}
	else
	{	s =  "OSC message \"" +  
			String((msg->GetOscAddress().GetBuffer())) 
			+ "\" contains.";
	}

	eilog(s);
		
	for (int i = 0; i < nStr; i++ )
	{	
		s =  " str[" + 
		StringConverter::toString(i) + "]" + 
		String((msg->GetString(i).GetBuffer()));
		eilog(s);
	}		     
	for (int i = 0; i < nInt; i++ )
	{
		s =  " int[" + 
		StringConverter::toString(i) + "]" + 
		StringConverter::toString(msg->GetInt(i));
		eilog(s);
	}
	for (int i = 0; i < nFlt; i++ )
	{
		s =  " flt[" + 
		StringConverter::toString(i) + "]" + 
		StringConverter::toString(msg->GetFloat(i));
		eilog(s);
	}
}

/** Initializes the underlying UDP network layer.
 * \param port
 * Port at which NetworkInterface should listen for OSC data.
 * \returns
 * Error code on error, WOS_ERR_NO_ERROR on success.
 */
NetworkInterface::NetworkInterfaceErrors NetworkInterface::NetworkInit(int port)
{
	int err;
	const int REUSE_TRUE = 1, BROADCAST_TRUE = 1;
	
#if defined( WIN32 )
	// fucking windows winsock startup
	WSADATA wsa;
	err = WSAStartup(MAKEWORD(2,0),&wsa);
 	if ( err != 0 )
	{
		eilog( "Error starting Windows socket subsystem.") ;
		return WOS_ERR_SOCKET_WSASTART;
	}
#endif	// #if defined( WIN32 )

	// create socket
	m_hSocket = socket (AF_INET, SOCK_DGRAM, PROTOCOL_UDP);
	if (m_hSocket < 0)
	{
		eilog("Create socket error.");
		return WOS_ERR_SOCKET_CREATE;
	}
	
	// initialize server address to localhost:port
	m_serverAddr.m_addr.sin_family         = AF_INET;
	m_serverAddr.m_addr.sin_addr.s_addr    = htonl (INADDR_ANY);
	m_serverAddr.m_addr.sin_port           = htons (port);
	
	// set socket to reuse the address
	err = setsockopt(
		m_hSocket,
		SOL_SOCKET,
		SO_REUSEADDR,
		(const char*)&REUSE_TRUE,
		sizeof(REUSE_TRUE));
	if ( err != 0 )
	{
		eilog( "Error setting socket reuse.") ;
		return WOS_ERR_SOCKET_REUSE;
	}
	// enable broadcasting for this socket
	setsockopt(
		m_hSocket,
		SOL_SOCKET,
		SO_BROADCAST,
		(const char*)&BROADCAST_TRUE,
		sizeof(BROADCAST_TRUE));
	if ( err != 0 )
	{
		eilog("Error setting socket broadcast.") ;
		return WOS_ERR_SOCKET_BROADCAST;
	}
	
	// disable blocking, polling is used in this example.
#if defined ( _UNIX_ )
	err = fcntl(m_hSocket, F_SETFL, O_NONBLOCK);
#elif defined ( WIN32 )
	// disable blocking (for this example)
	unsigned long val = 1;
	err = ioctlsocket(m_hSocket, FIONBIO , &val);
#endif
	if ( err != 0 )
	{
		eilog( "Error setting socket unblock.") ;
		return WOS_ERR_SOCKET_BLOCK;
	}
	
	// bind for listening
	err = bind (
		m_hSocket,
		(struct sockaddr *)&m_serverAddr.m_addr,
		WOS_SIZE_NRA );
	if ( err != 0 )
	{
		eilog( "Error socket bind") ;
		return WOS_ERR_SOCKET_BIND;
	}

	return WOS_ERR_NO_ERROR;

}

/** Stops the network interface.
 * \returns
 * WOS_ERR_NO_ERROR on success.
 */
NetworkInterface::NetworkInterfaceErrors NetworkInterface::NetworkHalt()
{
	// close socket... 
#if defined (_UNIX_)
	close(m_hSocket);
#elif defined( WIN32 )
	closesocket(m_hSocket);
	WSACleanup();
#endif
	return WOS_ERR_NO_ERROR;
}

/** Check if there is an UDP packet. On packet arrival it will be passed 
 * to the OSC receiver. It returns if there are no more packets available.
 * \returns
 * WOS_ERR_NO_ERROR if no error occurred.
 */
NetworkInterface::NetworkInterfaceErrors NetworkInterface::CheckForPackets()
{
	// as long there are packets waiting
    bool morePackets = 1;
    while(morePackets)
    {

		// has to be allocated on heap, because is reference
		// counted
		TheNetReturnAddress* nra = new TheNetReturnAddress;
		nra->m_addr.sin_family         = AF_INET;
		nra->m_addr.sin_addr.s_addr    = htonl (INADDR_ANY);
		nra->m_addr.sin_port           = htons (0);
	
		// receive from network
		int nReceived = recvfrom(
			m_hSocket,
			m_rxbuffer,
			WOS_MAX_RX_UDP_PACKET,
			0,
			(struct sockaddr*)&nra->m_addr,
			&WOS_SIZE_NRA);
	
		// nReceived is the number of bytes actually read from 
		// network interface
		
		if (nReceived > 0)
		{
			NetworkReceive ( m_rxbuffer, nReceived, nra);
		}
		else
		{
			delete nra;
			morePackets = false;	// stop receiving process
		}
		
    }
	return WOS_ERR_NO_ERROR;
}
